﻿#ifdef __cplusplus
 extern "C" {
 #endif
#include <windows.h>
#include <assert.h>
#include <stdlib.h>
#include <stdio.h>
#include <tchar.h>
#include <strsafe.h>
#include <stdint.h>
#include <setupapi.h>
#include <initguid.h>
#include <winioctl.h>
#include <io.h>
 //#include <AtlBase.h>
#include "XdmaPublic.h"
#include "PcieFun.h"

#pragma comment(lib, "SetupAPI.lib")


#define FPGA_DDR_START_ADDR 0x10000000


#define C2H0_NAME "c2h_0"
#define H2C0_NAME "h2c_0"
#define USER_NAME "user"
#define EVENT0_NAME "event_0"
#define EVENT1_NAME "event_1"

#define MAX_BYTES_PER_TRANSFER 0x800000

HANDLE c2h0_device;
HANDLE h2c0_device;
HANDLE user_device;
HANDLE event0_device;
HANDLE event1_device;

unsigned char *h2c_align_mem_tmp;
unsigned char *c2h_align_mem_tmp;

static int verbose_msg(const char* const fmt, ...) {

    int ret = 0;
    va_list args;
    if (1) {
        va_start(args, fmt);
        ret = vprintf(fmt, args);
        va_end(args);
    }
    return ret;

}
static BYTE* allocate_buffer(size_t size, size_t alignment) {

    if(size == 0) {
        size = 4;
    }

    if (alignment == 0) {
        SYSTEM_INFO sys_info;
        GetSystemInfo(&sys_info);
        alignment = sys_info.dwPageSize;
        //printf("alignment = %d\n",alignment);
    }
    verbose_msg("Allocating host-side buffer of size %d, aligned to %d bytes\n", size, alignment);
    return (BYTE*)_aligned_malloc(size, alignment);

}

static int get_devices(GUID guid, char* devpath, size_t len_devpath) {

    SP_DEVICE_INTERFACE_DATA device_interface;
    PSP_DEVICE_INTERFACE_DETAIL_DATA dev_detail;
    DWORD index;
    HDEVINFO device_info;
    wchar_t tmp[256];
    device_info = SetupDiGetClassDevs((LPGUID)&guid, NULL, NULL, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE);
    if (device_info == INVALID_HANDLE_VALUE) {
        fprintf(stderr, "GetDevices INVALID_HANDLE_VALUE\n");
        exit(-1);
    }

    device_interface.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA);
    // enumerate through devices

    for (index = 0; SetupDiEnumDeviceInterfaces(device_info, NULL, &guid, index, &device_interface); ++index) {

        // get required buffer size
        ULONG detailLength = 0;
        if (!SetupDiGetDeviceInterfaceDetail(device_info, &device_interface, NULL, 0, &detailLength, NULL) && GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
            fprintf(stderr, "SetupDiGetDeviceInterfaceDetail - get length failed\n");
            break;
        }

        // allocate space for device interface detail
        dev_detail = (PSP_DEVICE_INTERFACE_DETAIL_DATA)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, detailLength);
        if (!dev_detail) {
            fprintf(stderr, "HeapAlloc failed\n");
            break;
        }
        dev_detail->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA);

        // get device interface detail
        if (!SetupDiGetDeviceInterfaceDetail(device_info, &device_interface, dev_detail, detailLength, NULL, NULL)) {
            fprintf(stderr, "SetupDiGetDeviceInterfaceDetail - get detail failed\n");
            HeapFree(GetProcessHeap(), 0, dev_detail);
            break;
        }

        StringCchCopy(tmp, len_devpath, dev_detail->DevicePath);
        wcstombs(devpath, tmp,256);
        HeapFree(GetProcessHeap(), 0, dev_detail);
    }

    SetupDiDestroyDeviceInfoList(device_info);

    return index;
}

static int open_devices()
{
    // get device path from GUID
    char device_path[MAX_PATH + 1] = "";
    char device_base_path[MAX_PATH + 1] = "";
    wchar_t device_path_w[MAX_PATH + 1] ;
    DWORD num_devices = get_devices(GUID_DEVINTERFACE_XDMA, device_base_path, sizeof(device_base_path));
    verbose_msg("Devices found: %d\n", num_devices);
    if (num_devices < 1)
    {
        printf("error\n");
    }
    // extend device path to include target device node (xdma_control, xdma_user etc)
    verbose_msg("Device base path: %s\n", device_base_path);
    strcpy_s(device_path, sizeof device_path, device_base_path);
    strcat_s(device_path, sizeof device_path, "\\");
    strcat_s(device_path, sizeof device_path, C2H0_NAME);
    verbose_msg("Device node: %s\n", C2H0_NAME);
    // open device file
    mbstowcs(device_path_w, device_path,sizeof(device_path));
    c2h0_device = CreateFile(device_path_w, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
    if (c2h0_device == INVALID_HANDLE_VALUE)
    {
        fprintf(stderr, "Error opening device, win32 error code: %ld\n", GetLastError());
        //
    }
    memset(device_path,0,sizeof(device_path));
    strcpy_s(device_path, sizeof device_path, device_base_path);
    strcat_s(device_path, sizeof device_path, "\\");
    strcat_s(device_path, sizeof device_path, H2C0_NAME);
    verbose_msg("Device node: %s\n", H2C0_NAME);
    // open device file
    mbstowcs(device_path_w, device_path, sizeof(device_path));
    h2c0_device = CreateFile(device_path_w, GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
    if (h2c0_device == INVALID_HANDLE_VALUE)
    {
        fprintf(stderr, "Error opening device, win32 error code: %ld\n", GetLastError());
        //
    }
    memset(device_path,0,sizeof(device_path));
    strcpy_s(device_path, sizeof device_path, device_base_path);
    strcat_s(device_path, sizeof device_path, "\\");
    strcat_s(device_path, sizeof device_path, USER_NAME);
    verbose_msg("Device node: %s\n", USER_NAME);
    // open device file
    mbstowcs(device_path_w, device_path, sizeof(device_path));
    user_device = CreateFile(device_path_w, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
    if (user_device == INVALID_HANDLE_VALUE)
    {
        fprintf(stderr, "Error opening device, win32 error code: %ld\n", GetLastError());
        //
    }
    memset(device_path,0,sizeof(device_path));
    strcpy_s(device_path, sizeof device_path, device_base_path);
    strcat_s(device_path, sizeof device_path, "\\");
    strcat_s(device_path, sizeof device_path, EVENT0_NAME);
    verbose_msg("Device node: %s\n", EVENT0_NAME);
    // open device file
    mbstowcs(device_path_w, device_path, sizeof(device_path));
    event0_device = CreateFile(device_path_w, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
    if (event0_device == INVALID_HANDLE_VALUE)
    {
        fprintf(stderr, "Error opening device, win32 error code: %ld\n", GetLastError());
        //
    }
    memset(device_path,0,sizeof(device_path));
    strcpy_s(device_path, sizeof device_path, device_base_path);
    strcat_s(device_path, sizeof device_path, "\\");
    strcat_s(device_path, sizeof device_path, EVENT1_NAME);
    verbose_msg("Device node: %s\n", EVENT1_NAME);
    // open device file
    mbstowcs(device_path_w, device_path, sizeof(device_path));
    event1_device = CreateFile(device_path_w, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
    if (event1_device == INVALID_HANDLE_VALUE)
    {
        fprintf(stderr, "Error opening device, win32 error code: %ld\n", GetLastError());
        //
    }
    return 1;
}

static int read_device(HANDLE device,long address,DWORD size,BYTE *buffer)
{
    DWORD rd_size = 0;

    unsigned int transfers;
    unsigned int i;
    if (INVALID_SET_FILE_POINTER == SetFilePointer(device, address, NULL, FILE_BEGIN)) {
        fprintf(stderr, "Error setting file pointer, win32 error code: %ld\n", GetLastError());
        return -3;
    }
    transfers = (unsigned int)(size/MAX_BYTES_PER_TRANSFER);
    for(i=0;i<transfers;i++)
    {
        if (!ReadFile(device, (void *)(buffer+i*MAX_BYTES_PER_TRANSFER), (DWORD)MAX_BYTES_PER_TRANSFER, &rd_size, NULL))
        {
            return -1;
        }
        if(rd_size!=MAX_BYTES_PER_TRANSFER)
        {
            return -2;
        }
    }
    if (!ReadFile(device, (void *)(buffer+i*MAX_BYTES_PER_TRANSFER), (DWORD)(size - i*MAX_BYTES_PER_TRANSFER), &rd_size, NULL))
    {
        return -1;
    }
    if(rd_size!=(size-i*MAX_BYTES_PER_TRANSFER))
    {
        return -2;
    }

    return size;
}

static int write_device(HANDLE device,long address,DWORD size,BYTE *buffer)
{
    DWORD wr_size = 0;
    unsigned int transfers;
    unsigned int i;
    transfers = (unsigned int)(size/MAX_BYTES_PER_TRANSFER);
    //printf("transfers = %d\n",transfers);
    if (INVALID_SET_FILE_POINTER == SetFilePointer(device, address, NULL, FILE_BEGIN)) {
        fprintf(stderr, "Error setting file pointer, win32 error code: %ld\n", GetLastError());
        return -3;
    }
    for(i=0;i<transfers;i++)
    {
        if (!WriteFile(device, (void *)(buffer+i*MAX_BYTES_PER_TRANSFER), MAX_BYTES_PER_TRANSFER, &wr_size, NULL))
        {
            return -1;
        }
        if(wr_size!=MAX_BYTES_PER_TRANSFER)
        {
            return -2;
        }
    }
    if (!WriteFile(device, (void *)(buffer+i*MAX_BYTES_PER_TRANSFER), (DWORD)(size-i*MAX_BYTES_PER_TRANSFER), &wr_size, NULL))
    {
        return -1;
    }
    if(wr_size!=(size-i*MAX_BYTES_PER_TRANSFER))
    {
        return -2;
    }
    return size;
}
static int seek_device(HANDLE device,long address)
{
    if (INVALID_SET_FILE_POINTER == SetFilePointer(device, address, NULL, FILE_BEGIN)) {
        fprintf(stderr, "Error setting file pointer, win32 error code: %ld\n", GetLastError());
        return -1;
    }
    return 0;
}

void read_control(long addr,uint32_t *val )
{
    read_device(user_device,addr,4,(BYTE *)val);
}

void write_control(long addr,uint32_t val)
{
    write_device(user_device,addr,4,(BYTE *)&val);
}


void put_data_to_fpga_ddr(long fpga_ddr_addr,unsigned char *buffer,unsigned int len)
{

    memcpy(h2c_align_mem_tmp,buffer,len);
    write_device(h2c0_device,fpga_ddr_addr,len,h2c_align_mem_tmp);
}


void get_data_from_fpga_ddr(long fpga_ddr_addr,unsigned char *buffer,unsigned int len)
{
    read_device(c2h0_device,fpga_ddr_addr,len,buffer);
}

int pcie_init()
{
    int res = 1;
    res = open_devices();
    h2c_align_mem_tmp = allocate_buffer(0x800000,4096);
    c2h_align_mem_tmp = allocate_buffer(0x800000,4096);
    if(NULL == h2c_align_mem_tmp || NULL == c2h_align_mem_tmp)
        return 0;
    return res;
}
void pcie_deinit()
{
    CloseHandle(user_device);
    CloseHandle(event0_device);
    CloseHandle(event1_device);
    CloseHandle(c2h0_device);
    CloseHandle(h2c0_device);
    _aligned_free(h2c_align_mem_tmp);
    _aligned_free(c2h_align_mem_tmp);
}


#ifdef __cplusplus
}
#endif
